package com.shxzz.easy12306.services.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.shxzz.easy12306.framework.starter.cache.StringRedisTemplateProxy;
import com.shxzz.easy12306.framework.starter.convention.exceptions.ClientException;
import com.shxzz.easy12306.framework.starter.user.core.UserContext;
import com.shxzz.easy12306.services.user.common.constant.RedisKeyConstant;
import com.shxzz.easy12306.services.user.dao.entity.*;
import com.shxzz.easy12306.services.user.dao.mapper.*;
import com.shxzz.easy12306.services.user.dto.req.UserDeletionReqDTO;
import com.shxzz.easy12306.services.user.dto.req.UserUpdateReqDTO;
import com.shxzz.easy12306.framework.starter.common.dto.user.UserQueryActualRespDTO;
import com.shxzz.easy12306.services.user.dto.resp.UserQueryRespDTO;
import com.shxzz.easy12306.services.user.service.UserService;
import com.shxzz.easy12306.services.user.toolkit.UserReuseUtil;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.Optional;

/**
 * 用户管理接口实现
 *
 * @author shxzz
 * @create 2024-02-28-21:18
 */
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService {

    private final UserDeletionMapper userDeletionMapper;
    private final UserMapper userMapper;
    private final UserPhoneMapper userPhoneMapper;
    private final UserEmailMapper userEmailMapper;
    private final PassengerMapper passengerMapper;
    private final UserReuseMapper userReuseMapper;


    private final RedissonClient redissonClient;
    private final StringRedisTemplateProxy stringRedisTemplateProxy;

    @Override
    public Integer queryUserDeletionNum(Integer idType, String idCard) {
        LambdaQueryWrapper<UserDeletionDO> userDeletionDOLambdaQueryWrapper = Wrappers.lambdaQuery(UserDeletionDO.class)
                .eq(UserDeletionDO::getIdType, idType)
                .eq(UserDeletionDO::getIdCard, idCard);
        Long count = userDeletionMapper.selectCount(userDeletionDOLambdaQueryWrapper);
        return Optional.ofNullable(count).map(Long::intValue).orElse(0);
    }

    @Override
    public UserQueryRespDTO queryUserByUsername(String username) {
        return BeanUtil.toBean(queryActualUserByUsername(username), UserQueryRespDTO.class);
    }

    @Override
    public UserQueryActualRespDTO queryActualUserByUsername(String username) {
        UserDO userDO = userMapper.selectOne(Wrappers.lambdaQuery(UserDO.class).eq(UserDO::getUsername, username));
        if (userDO == null) {
            throw new ClientException("用户名不存在");
        }
        return BeanUtil.toBean(userDO, UserQueryActualRespDTO.class);
    }

    @Override
    public void update(UserUpdateReqDTO requestParam) {
        if (!Objects.equals(UserContext.getUsername(), requestParam.getUsername())) {
            throw new ClientException("非法操作");
        }
        if(CharSequenceUtil.isNotEmpty(requestParam.getEmail())) {
            throw new ClientException("暂不支持修改");
        }
        userMapper.update(BeanUtil.toBean(requestParam, UserDO.class), Wrappers.lambdaUpdate(UserDO.class).eq(UserDO::getUsername, requestParam.getUsername()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deletion(UserDeletionReqDTO requestParam) {
        String username = requestParam.getUsername();
        if (!Objects.equals(UserContext.getUsername(), username)) {
            throw new ClientException("非法操作");
        }
        RLock lock = redissonClient.getLock(RedisKeyConstant.LOCK_USER_DELETION_PREFIX + username);
        lock.lock();
        try {
            long deletionTime = System.currentTimeMillis();

            UserQueryActualRespDTO userQueryActualRespDTO = queryActualUserByUsername(username);

            UserDO userDO = new UserDO();
            userDO.setDeletionTime(deletionTime);
            userDO.setDelFlag(1);
            LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class)
                    .eq(UserDO::getUsername, username);
            userMapper.update(userDO, updateWrapper);

            UserPhoneDO userPhoneDO = new UserPhoneDO();
            userPhoneDO.setDeletionTime(deletionTime);
            userPhoneDO.setDelFlag(1);
            LambdaUpdateWrapper<UserPhoneDO> userPhoneDOLambdaUpdateWrapper = Wrappers.lambdaUpdate(UserPhoneDO.class)
                    .eq(UserPhoneDO::getUsername, username);
            userPhoneMapper.update(userPhoneDO, userPhoneDOLambdaUpdateWrapper);

            if (CharSequenceUtil.isNotEmpty(userQueryActualRespDTO.getEmail())) {
                UserEmailDO userEmailDO = new UserEmailDO();
                userEmailDO.setDeletionTime(deletionTime);
                userEmailDO.setDelFlag(1);
                LambdaUpdateWrapper<UserEmailDO> userEmailDOLambdaUpdateWrapper = Wrappers.lambdaUpdate(UserEmailDO.class)
                        .eq(UserEmailDO::getUsername, username);
                userEmailMapper.update(userEmailDO, userEmailDOLambdaUpdateWrapper);
            }

            PassengerDO passengerDO = new PassengerDO();
            passengerDO.setDeletionTime(deletionTime);
            passengerDO.setDelFlag(1);
            LambdaUpdateWrapper<PassengerDO> passengerDOLambdaUpdateWrapper = Wrappers.lambdaUpdate(PassengerDO.class)
                    .eq(PassengerDO::getUsername, username);
            passengerMapper.update(passengerDO, passengerDOLambdaUpdateWrapper);

            StringRedisTemplate stringRedisTemplate = stringRedisTemplateProxy.getInstance();
            String userReuseSetKey = RedisKeyConstant.USERNAME_REUSE_SET_PREFIX + UserReuseUtil.getUsernameReuseShardingIdx(username);
            stringRedisTemplate.opsForSet().add(userReuseSetKey, username);
            userReuseMapper.insert(new UserReuseDO(username));

            UserDeletionDO userDeletionDO = UserDeletionDO.builder()
                    .deletionTime(deletionTime)
                    .idType(userQueryActualRespDTO.getIdType())
                    .idCard(userQueryActualRespDTO.getIdCard())
                    .build();
            userDeletionMapper.insert(userDeletionDO);

            stringRedisTemplate.delete(UserContext.getUser().getToken());
        } finally {
            lock.unlock();
        }
    }
}
